package cn.edu.hznu.spring;

import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import javax.annotation.Resource;

public class SpringByAnnotation {
	private Map<String, Class> beanMap = new HashMap<String, Class>();
	
	private String lowerCaseFirstCharOfString(String str) {
		char firstCharAfterUpLower = (char) (str.charAt(0) + 32);
		return firstCharAfterUpLower + str.substring(1);
	}
	
	private String upCaseFirstCharOfString(String str) {
		char firstCharAfterUpLower = (char) (str.charAt(0) - 32);
		return firstCharAfterUpLower + str.substring(1);
	}
	
	public void load() throws URISyntaxException {
		String packageName = "cn.edu.hznu.spring";
		Arrays.stream(Paths.get(this.getClass().getResource("").toURI())
					       .toFile()
					       .listFiles())
			  .filter(file -> file.getName().endsWith(".class"))
			  .forEach(file -> {
				  System.out.println(file.getName());
				  String className = file.getName().substring(0, file.getName().length() - ".class".length());
				  String fullClassName = packageName + "." + className;
				  try {
					  Class clazz = Class.forName(fullClassName);
					  Optional.ofNullable(clazz.getAnnotation(Component.class))
					  		  .ifPresent(annotion -> {
					  			  Component cop = (Component) annotion;
					  			  String beanId = cop.value();
					  			  if (cop.value().isEmpty()) {
					  				  beanId = lowerCaseFirstCharOfString(className);
					  			  }
					  			  beanMap.put(beanId, clazz);
					  		  });
					  
				  } catch (ClassNotFoundException e) {
					  e.printStackTrace();
				  }
			  });
	}
	
	public Object getBean(String beanId) throws InstantiationException, IllegalAccessException {
		if (null == beanId) return null;
		Class clazz = beanMap.get(beanId);
		Object obj = clazz.newInstance();
		Arrays.stream(clazz.getDeclaredFields())
			  .forEach(field -> {
				  try {
					  Method m = clazz.getMethod("set"+ upCaseFirstCharOfString(field.getName()), field.getType());
					  Optional.ofNullable(field.getAnnotation(Value.class))
							  .ifPresent(v -> {
								  String propertyValue = v.value();
								  try {
									  m.invoke(obj, propertyValue);
								  } catch (Exception e) {
									  e.printStackTrace();
								  }
							  });
					  Optional.ofNullable(field.getAnnotation(Resource.class))
					  		  .ifPresent(r -> {
					  			  String propertyBeanId = r.name();
					  			  if (propertyBeanId.isEmpty()) {
					  				  propertyBeanId = lowerCaseFirstCharOfString(field.getType().getSimpleName()); 
					  			  }
					  			  try {
									  Object propertyObj = getBean(propertyBeanId);
									  m.invoke(obj, propertyObj);
								  } catch (Exception e) {
									  e.printStackTrace();
								  }
					  		  });
				  } catch (Exception e) {
					  e.printStackTrace();
				  }
			  });
		return obj;
	}
	
	public static void main(String[] args) {
		SpringByAnnotation spring = new SpringByAnnotation();
		try {
			spring.load();
			BeanA beanA = (BeanA) spring.getBean("beanA");
			System.out.println(beanA.getTitle());
			System.out.println(beanA.getBeanB().getTitle());
		} catch (InstantiationException | IllegalAccessException | URISyntaxException e) {
			e.printStackTrace();
		}		
	}

}
